package DB.Inscricao;

import DB.DBManager;
import DB.EntityBase;
import DB.Evento.ControllerEvento;
import DB.Evento.EventoTable;
import DB.Usuario.ControllerUsuario;
import DB.Usuario.UsuariosTable;
import DB.Util;
import Model.Boletos;
import Model.Evento;
import Model.Inscricao;
import Model.Usuario;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Calendar;

/**
 *
 * @author pgrabin
 */
public class ControllerInscricao extends EntityBase<Inscricao> {

    private DBManager db;
    private Connection conn;
    private static ControllerInscricao eControl;

    public static ControllerInscricao getInstance() {
        if (eControl == null) {
            eControl = new ControllerInscricao();
        }

        return eControl;
    }

    public ControllerInscricao() {
        db = DBManager.getInstance();
        conn = db.getConn();
    }

    /**
     * Recebe os dados da inscrição a ser gravada e chama o mét odo responsavel
     * pela gravação no banco de dados.
     *
     * @param e O EVENTO ao qual a inscrição se refere.
     * @param u O USUARIO ao qual a inscrição se refere
     * @return o ID que a inscrição recebeu.
     * @throws java.sql.SQLException
     */
    public long inscricao(Evento e, Usuario u) throws SQLException {
        return gravaInscricao(e.getId(), u.getId());
    }

    /**
     * Recebe os dados da inscrição a ser gravada e chama o mét odo responsavel
     * pela gravação no banco de dados.
     *
     * @param eventoID O id do EVENTO ao qual a inscrição se refere.
     * @param usuarioID O id do USUARIO ao qual a inscrição se refere.
     * @return O ID que a INSCRIÇÃO recebeu.
     * @throws java.sql.SQLException
     */
    public long inscricao(long eventoID, long usuarioID) throws SQLException {
        return gravaInscricao(eventoID, usuarioID);
    }

    /**
     * Recebe os dados da inscrição a ser gravada e chama o mét odo responsavel
     * pela gravação no banco de dados.
     *
     * @param i A inscrição que será gravada.
     * @return
     * @throws java.sql.SQLException
     */
    public long inscricao(Inscricao i) throws SQLException {
        return gravaInscricao(i.getIdEvento(), i.getIdParticipante());
    }

    /**
     * Faz a gravação da inscrição no banco de dados.
     *
     * @param eventoID
     * @param usuarioID
     * @return
     * @throws SQLException
     */
    private long gravaInscricao(long eventoID, long usuarioID) throws SQLException {
        ResultSet generatedKeys = null;
        long idRetornado = 0;
        Calendar c = Calendar.getInstance();

        ControllerEvento ce = ControllerEvento.getInstance();
        int vagasDoEvento = ce.getVagasById(eventoID);
        ControllerInscricao ci = ControllerInscricao.getInstance();
        int usuariosInscritos = ci.getQuantidadeInscricoesByID(eventoID);

        System.out.println("Evento tem " + vagasDoEvento + " vagas.");
        System.out.println("Evento já tem " + usuariosInscritos + " usuarios inscritos");

        if (usuariosInscritos >= vagasDoEvento) {
            throw new SQLException("Limite de vagas atingido.");
        }

        PreparedStatement ps = conn.prepareStatement("INSERT INTO " + InscricaoTable.TABLE_NAME + " VALUES (null, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
        ps.setLong(1, eventoID);
        ps.setLong(2, usuarioID);
        ps.setString(3, Util.getFormatoData().format(c.getTime()));

        int affedtedRows = ps.executeUpdate();
        if (affedtedRows == 0) {
            throw new SQLException("Inseriong Inscrição failed, ro nows affected.");
        }
        generatedKeys = ps.getGeneratedKeys();
        if (generatedKeys.next()) {
            idRetornado = generatedKeys.getLong(1);
        } else {
            throw new SQLException("Inseriong Inscrição failed, no generated key obtained.");
        }

        Boletos b = new Boletos(idRetornado);
        return idRetornado;
    }

    /**
     * Retorna todos os registros da tabela.
     *
     * @return Um arrayList<Inscricao> contendo todos os registros.
     */
    @Override
    public ArrayList getAll() {
        ArrayList<Inscricao> inscricoes = new ArrayList<>();
        ResultSet rs;

        try {
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + InscricaoTable.TABLE_NAME);
            rs = ps.executeQuery();

            while (rs.next()) {
                Inscricao resultInscricao = getFromResultSet(rs);
                inscricoes.add(resultInscricao);
            }
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return inscricoes;
    }

    /**
     * Converte o resultSet do banco em uma Inscricao
     *
     * @param rs o resultSet
     * @return a Inscricao retornada
     */
    @Override
    public Inscricao getFromResultSet(ResultSet rs) {
        Inscricao i = null;

        try {
            int id = rs.getInt(InscricaoTable.COLUMN_ID);
            int idEvento = rs.getInt(InscricaoTable.COLUMN_IDEVENTO);
            int idParticipante = rs.getInt(InscricaoTable.COLUMN_IDPARTICIPANTE);
            String dataInscricao = rs.getString(InscricaoTable.COLUMN_DATAINSCRICAO);

            i = new Inscricao(id, idEvento, idParticipante, dataInscricao);
//            System.out.println("inscricaoGetFromResultSet retornando " + i.toString());
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }
        return i;
    }

    /**
     * Retorna a INSCRIÇÃO cujo ID é igual ao ID informado, Ou null caso não
     * encontre alguma INSCRICAO com aquele ID.
     *
     * @param id ID da INSCRICAO que será retornado.
     * @return Retorna o INSCRICAO com o ID informado
     */
    @Override
    public Inscricao getByID(long id) {
        ResultSet rs;
        Inscricao i = null;

        try {
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + InscricaoTable.TABLE_NAME + " WHERE ID = ?");
            ps.setLong(1, id);

            rs = ps.executeQuery();

            if (rs.next()) {
                i = getFromResultSet(rs);
            } else {
                throw new SQLException("Inscrição não encontrada.");
            }
        } catch (SQLException ex) {
            ex.printStackTrace(System.err);
        }

        return i;
    }

    /**
     * Retorna a quantidade de inscrições em um evento.
     *
     * @param idInscricao
     * @return
     */
    public int getQuantidadeInscricoesByID(long idInscricao) {
        int retorno = 0;
        ResultSet rs;

        String query = "SELECT COUNT(*) "
                + "FROM " + InscricaoTable.TABLE_NAME
                + " WHERE " + InscricaoTable.COLUMN_IDEVENTO + " = ? ";

//        System.out.println(query);
        try {
            PreparedStatement ps = conn.prepareStatement(query);
            ps.setLong(1, idInscricao);

            rs = ps.executeQuery();

            if (rs.next()) {
                retorno = rs.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return retorno;
    }

    /**
     * Retorna o ID do USUARIO que consta na Inscrição com o ID informado.
     *
     * @param idInscricao
     * @return
     * @throws SQLException
     */
    public Usuario getUsuarioByID(long idInscricao) throws SQLException {
        Usuario u = null;
        ResultSet rs;

        String query = "SELECT * "
                + "FROM " + UsuariosTable.TABLE_NAME + " u, " + InscricaoTable.TABLE_NAME + " i "
                + "WHERE i." + InscricaoTable.COLUMN_ID + " = ? "
                + "AND i." + InscricaoTable.COLUMN_IDPARTICIPANTE + " = u." + UsuariosTable.COLUMN_ID;
//        System.out.println(query);
        PreparedStatement ps = conn.prepareStatement(query);
        ps.setLong(1, idInscricao);

        rs = ps.executeQuery();

        if (rs.next()) {
            ControllerUsuario cu = ControllerUsuario.getInstance();
            u = cu.getFromResultSet(rs);
        } else {
            throw new SQLException("Retornou nada.");
        }

        if (u == null) {
            throw new SQLException("Inscrição não encontrada.");
        }

        return u;
    }

    /**
     * Retorna o ID do EVENTO que consta na Inscrição com o ID informado.
     *
     * @param idInscricao
     * @return
     * @throws SQLException
     */
    public Evento getEventoByID(long idInscricao) throws SQLException {
        Evento e = null;
        ResultSet rs;

        String query = "SELECT * "
                + "FROM " + EventoTable.TABLE_NAME + " e, " + InscricaoTable.TABLE_NAME + " i "
                + "WHERE i." + InscricaoTable.COLUMN_ID + " = ? "
                + "AND i." + InscricaoTable.COLUMN_IDEVENTO + " = e." + EventoTable.COLUMN_ID;
//        System.out.println(query);
        PreparedStatement ps = conn.prepareStatement(query);
        ps.setLong(1, idInscricao);

        rs = ps.executeQuery();

        if (rs.next()) {
            ControllerEvento ce = ControllerEvento.getInstance();
            e = ce.getFromResultSet(rs);
        } else {
            throw new SQLException("Retornou nada.");
        }

        if (e == null) {
            throw new SQLException("Inscrição não encontrada.");
        }

        return e;
    }

    /**
     * Deleta da base de dados o registro com o ID informado
     *
     * @param id O ID da INSCRIÇÃO que deve ser deletada.
     * @return Quantidade de linhas afetadas.
     */
    @Override
    public int delete(long id) {
        int resultadoDelete = 0;

        try {
            PreparedStatement ps = conn.prepareStatement("DELETE FROM " + InscricaoTable.TABLE_NAME + " WHERE " + InscricaoTable.COLUMN_ID + " = ?");
            ps.setLong(1, id);

            resultadoDelete = ps.executeUpdate();
            if (resultadoDelete == 0) {
                throw new SQLException("No record found for deletion!");
            } else if (resultadoDelete > 1) {
                throw new SQLException("More than one row deleted!");
            } else {
                System.out.println("Inscrição código " + id + " deletada!");
            }

        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return resultadoDelete;
    }

    /**
     * Atualiza o registro na base de dados do usuario recebido como parametro,
     * tomando como base o ID.
     *
     * @param i A INSCRICAO que deve ter suas informações atualizadas.
     * @return Quantidade de linhas afetadas.
     */
    @Override
    public long update(Inscricao i) {
        ResultSet generatedKeys = null;
        long affectedRows = 0;

        try {
            PreparedStatement ps = conn.prepareStatement("UPDATE " + InscricaoTable.TABLE_NAME
                    + " SET idEvento = ?,"
                    + " idParticipante = ?,"
                    + " dataInscricao = ?"
                    + " WHERE id = ?", Statement.RETURN_GENERATED_KEYS);

            ps.setLong(1, i.getIdEvento());
            ps.setLong(2, i.getIdParticipante());
            ps.setString(3, i.getDataInscricao());
            ps.setLong(4, i.getId());

//            System.out.println(e.getId() + " - " + e.getNome());
            affectedRows = ps.executeUpdate();
            if (affectedRows == 0) {
                throw new SQLException("Update failed, no rows affected");
            } else if (affectedRows > 1) {
//                        throw new SQLException("More than one rows updated");
            }
        } catch (SQLException ex) {
            ex.printStackTrace(System.err);
        }

        return affectedRows;
    }

    /**
     * Todos os eventos em que o usuario com o ID informado está matriculado.
     *
     * @param userID
     * @return
     */
    public ArrayList<Evento> getAllEventsFromUser(long userID) {
        ArrayList<Evento> eventos = new ArrayList<>();
        ResultSet rs;

        try {
            String query = "SELECT * "
                    + "FROM " + EventoTable.TABLE_NAME + " e, " + InscricaoTable.TABLE_NAME + " i"
                    + " WHERE e." + EventoTable.COLUMN_ID + " = i." + InscricaoTable.COLUMN_IDEVENTO
                    + " AND i." + InscricaoTable.COLUMN_IDPARTICIPANTE + " = ?";
//            System.out.println(query);

            PreparedStatement ps = conn.prepareStatement(query);

            ps.setLong(1, userID);

            rs = ps.executeQuery();

            ControllerEvento ce = ControllerEvento.getInstance();
            while (rs.next()) {
                Evento i = ce.getFromResultSet(rs);
                eventos.add(i);
            }
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return eventos;
    }

    /**
     * Retorna todos os usuarios matriculados no evento cujo ID foi informado.
     *
     * @param eventoID
     * @return
     */
    public ArrayList<Usuario> getAllUsersFromEvent(long eventoID) {
        ArrayList<Usuario> usuarios = new ArrayList<>();
        ResultSet rs;

        try {
            String query = "SELECT * "
                    + "FROM " + UsuariosTable.TABLE_NAME + " u, " + InscricaoTable.TABLE_NAME + " i "
                    + "WHERE " + "u." + UsuariosTable.COLUMN_ID + " = " + "i." + InscricaoTable.COLUMN_IDPARTICIPANTE + " "
                    + "AND i." + InscricaoTable.COLUMN_IDEVENTO + " = ?";
//            System.out.println(query);

            PreparedStatement ps = conn.prepareStatement(query);
            ps.setLong(1, eventoID);

            rs = ps.executeQuery();

            ControllerUsuario cu = ControllerUsuario.getInstance();
            while (rs.next()) {
                Usuario u = cu.getFromResultSet(rs);
                usuarios.add(u);
            }
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return usuarios;
    }

    /**
     * Retorna a quantidade de registros na tabela.
     *
     * @return a quantidade de registros;
     */
    @Override
    public int getRowsCount() {
        return db.getTableCount(InscricaoTable.TABLE_NAME);
    }

    /**
     * Adiciona alguns registros padrão na tabela, para testes e demonstrações
     * de usabilidade.
     */
    public void standardInscricoes() {
        ControllerInscricao ci = ControllerInscricao.getInstance();

//        // Encontro
//        for(int i = 1; i <= 1; i++) {
//            ci.inscricao(1, i);
//        }
//        
//        // Embarque
//        ci.inscricao(2, 1);
//        ci.inscricao(2, 2);
//        ci.inscricao(2, 5);
//        
//        // Chegada
//        ci.inscricao(3, 1);
//        ci.inscricao(3, 2);
//        
//        // Entrevista
//        ci.inscricao(4, 1);
//        
//        // Putaria
//        ci.inscricao(5, 4);
//        ci.inscricao(5, 5);
        System.out.println("Inscrições padrão inseridas!");
    }

    public static void main(String args[]) {

        ControllerInscricao cii = ControllerInscricao.getInstance();
        System.out.println(cii.getQuantidadeInscricoesByID(1));
        System.out.println(cii.getQuantidadeInscricoesByID(2));
        System.out.println(cii.getQuantidadeInscricoesByID(3));

//        ControllerUsuario cu = ControllerUsuario.getInstance();
//        ControllerEvento ce = ControllerEvento.getInstance();
//        ControllerInscricao ci = ControllerInscricao.getInstance();
//        
//        Usuario u = null;
//        Evento e = null;
//        
//        try {
//            u = cu.getByID(1);
//            e = ce.getByID(1);
//        }
//        catch (SQLException ex) {
//            ex.printStackTrace();
//        }
//        
//        System.out.println(u.toString());
//        System.out.println(e.toString());
//        
//        System.out.println("Tabela inscrições tem: " + ci.getRowsCount());
//        
//        long iID = ci.inscricao(1, 1);
//        ci.inscricao(1, 2);
//        ci.inscricao(1, 3);
//        
//        System.out.println("Inscrição recebeu o id: " + iID);
//        
//        Inscricao i = ci.getByID(iID);
//        
//        System.exit(0);
//        ControllerEvento ce = ControllerEvento.getInstance();
//        ControllerUsuario cu = ControllerUsuario.getInstance();
//        ControllerInscricao ci = ControllerInscricao.getInstance();
//        
//        try {
//            Inscricao i = ci.getByID(55);
//            System.out.println(i);
//            
//            Usuario u = ci.getUsuarioByID(55);
//            System.out.println(u);
//            
//            Evento e = ci.getEventoByID(55);
//            System.out.println(e);
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        /**
         * INSERT Evento e = new Evento("Evento de testes", "Descrição do evento
         * de testes", 150.25, 10, "2013-10-15", "2013-01-01", "2013-12-31",
         * "Endereço do evento de testes", "Cidade teste", "Estado", "Pais",
         * "93025660"); Usuario u = new Usuario("Paulo", "pgrabin2", "pgrabin",
         * 1, "02743273097", "plgrabin@gmail.com", "30/08/1990", "Spiga",
         * "Masculino", "Endereço", "Cidade", "Estado", "Pais", "CEP",
         * "35904041", "99576664");
         *
         * long idE = ce.inscricao(e); long idU = cu.inscricao(u);
         *
         * long idI = ci.inscricao(idE, idU);
         *
         */
//        long idE = 16;
//        long idU = 654;
//        ci.inscricao(idE, idU);
//        System.out.println(ci.getEventsFromUser(7));
//        System.out.println(ci.getAllUsersFromEvent(13));
//       GET
//        Inscricao i = ci.getByID(564);
//        System.out.println(i);
//        long idI = 1;
//        ci.delete(1);
        //GET ALL
//        System.out.println(ci.getAll());
        // ALL USERS FROM EVENT
//        System.out.println("Retornou " + ci.getAllUsersFromEvent(17).size());
//        System.out.println(ci.getAllUsersFromEvent(17));
//        
//        int id = 11;
//        // ALL EVENTS FROM USER
//        System.out.println("Retornou " + ci.getAllEventsFromUser(id).size());
//        System.out.println(ci.getAllEventsFromUser(id));
    }
}
